﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System.Threading;
using System.Net.Sockets;
using System.Net;

namespace ProjectNocturnalFool
{
    public enum Network_Message_Type
    {
        ChangeState,
        Message,
        Connect
    }

    public sealed class NetworkMgr
    {
        #region Fields

        public NetServer server;
        public NetClient client;
        
        public string clientName = Environment.UserName;

        public List<string> textMessageBuffer = new List<string>();

        public bool searchingForHost = false;

        #endregion

        #region Initialize

        /// <summary>
        /// Allocate ourselves.
        /// We have a private constructor, so no one else can.
        /// </summary>
        static readonly NetworkMgr _instance = new NetworkMgr();

        /// <summary>
        /// Access SiteStructure.Instance to get the singleton object.
        /// Then call methods on that instance.
        /// </summary>
        public static NetworkMgr Instance
        {
            get { return _instance; }
        }

        private NetworkMgr()
        {
            //server.
            //client.ServerConnection.Status += new EventHandler<NetConnectionStatus>(client_StatusChanged);
        }

        public void InitializeSinglePlayer()
        {
            // create a configuration
            NetPeerConfiguration hostConfig = new NetPeerConfiguration("TacticsSingle"); // needs to be same on client and server!
            hostConfig.Port = 12345;
          //  hostConfig.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
            hostConfig.EnableMessageType(NetIncomingMessageType.DiscoveryRequest);
            server = new NetServer(hostConfig);
            server.Start();

            NetPeerConfiguration clientConfig = new NetPeerConfiguration("TacticsSingle"); // needs to be same on client and server!
            clientConfig.EnableMessageType(NetIncomingMessageType.Data);
            client = new NetClient(clientConfig);
            client.Start();
            client.Connect("127.0.0.1", 12345);
        }

        private void InitializeMultiPlayer()
        {
            searchingForHost = true;
            client.Disconnect("Starting Multiplayer Search");
            NetPeerConfiguration clientConfig = new NetPeerConfiguration("TacticsSingle"); // needs to be same on client and server!
            clientConfig.EnableMessageType(NetIncomingMessageType.DiscoveryResponse);
            clientConfig.EnableMessageType(NetIncomingMessageType.Data);
            client = new NetClient(clientConfig);
            client.Start();
            
        }

        #endregion

        #region Updates
        public void Update(GameTime gameTime)
        {
            if (server != null)
            {
                UpdateServerNetwork();
            }
            if (client != null)
            {
                UpdateClientNetwork();
            }
            try
            {
                if (searchingForHost
                    && client.ServerConnection == null)
                {
                    IPHostEntry ipEntry = Dns.GetHostEntry(Dns.GetHostName());
                    IPAddress myAddr = ipEntry.AddressList[0];
                    byte[] myIPAddress = myAddr.GetAddressBytes();

                    //IPHostEntry hostEntry = Dns.GetHostEntry("192.168.2.14");
                    //string temp = hostEntry.HostName;
                    //IPAddress hostAddr = hostEntry.AddressList[0];
                    //IPEndPoint hostPoint = new IPEndPoint(hostAddr, 12345);

                   //bool isLocalAddress = IsLocalIpAddress("x.x.x.x");
                    client.Connect("192.168.2.14", 12345);
                  //  client.DiscoverKnownPeer(hostPoint);
                }
            }
            catch (SocketException se)
            {
                DebugMgr.Instance.addDebug("Socket exception is thrown in Connect: " + se.StackTrace, 9);
            }

            if (Constants.debugNetwork)
            {
                //DebugMgr.Instance.addDebug("Number of Players Connected: " + server.Connections.Count.ToString(), 0);

                //for (int i = 9; i > 1 + server.Connections.Count; i--)
                //{
                //    DebugMgr.Instance.addDebug("", i - 1);
                //}
                
                //for (int i = 0; i < server.Connections.Count; i++)
                //{
                //    DebugMgr.Instance.addDebug("Player " + i + " : " + server.Connections[i].RemoteEndPoint.ToString(), i + 1);
                //}
            }

            //if (GameLogic.Instance.gameStateLogic.state == GameStateMessage.MultiplayerOn)
            //{
            //    InitializeMultiPlayer();
            //}

        }


        private void UpdateServerNetwork()
        {
            NetIncomingMessage msg;
            while ((msg = server.ReadMessage()) != null)
            {
                switch (msg.MessageType)
                {
                    case NetIncomingMessageType.DiscoveryRequest:
                        //
                        // Server received a discovery request from a client; send a discovery response (with no extra data attached)
                        //
                        server.SendDiscoveryResponse(null, msg.SenderEndPoint);
                        break;
                    case NetIncomingMessageType.VerboseDebugMessage:
                    case NetIncomingMessageType.DebugMessage:
                    case NetIncomingMessageType.WarningMessage:
                    case NetIncomingMessageType.ErrorMessage:
                        Console.WriteLine(msg.ReadString());
                        break;
                    case NetIncomingMessageType.Data:
                        NetOutgoingMessage sendMsg = server.CreateMessage();
                        int msgType = msg.ReadVariableInt32();
                        if (msgType == (int)Network_Message_Type.Message)
                        {
                            sendMsg.WriteVariableInt32((int)Network_Message_Type.Message);
                             sendMsg.Write(msg.ReadString());
                             server.SendToAll(sendMsg, NetDeliveryMethod.ReliableOrdered);
                        }
                        
                        break;
                    case NetIncomingMessageType.StatusChanged:
                        NetConnectionStatus status = (NetConnectionStatus)msg.ReadByte();
                        if (status == NetConnectionStatus.Connected)
                        {
                        }

                        break;
                    default:
                        break;
                }
                server.Recycle(msg);
            }

        }

        private void UpdateClientNetwork()
        {

            // read messages
            NetIncomingMessage msg;
            while ((msg = client.ReadMessage()) != null)
            {
                switch (msg.MessageType)
                {
                    case NetIncomingMessageType.DiscoveryResponse:
                        // just connect to first server discovered
                        client.Connect(msg.SenderEndPoint);
                        break;
                    case NetIncomingMessageType.Data:
                        if (msg.ReadVariableInt32() == (int)Network_Message_Type.Message)
                        {
                            textMessageBuffer.Add(clientName);
                            textMessageBuffer.Add(msg.ReadString());
                        }
                        break;
                    case NetIncomingMessageType.StatusChanged:
                        if (msg.SenderConnection.Status == NetConnectionStatus.Disconnected)
                        {
                            if (!searchingForHost)
                            {
                                server.Shutdown("Restarting Single Player");
                                client.Disconnect("Starting Single Player");
                                InitializeSinglePlayer();
                                searchingForHost = false;
                            }
                        }

                        break;                        
                }
            }
        }

        #endregion

        #region Messaging
        public void SendMessage(Network_Message_Type type, string message)
        {

            NetOutgoingMessage sendMsg = client.CreateMessage();

            if (type == Network_Message_Type.Message 
                && client.ServerConnection != null)
            {
                sendMsg.WriteVariableInt32((int)Network_Message_Type.Message);
                sendMsg.Write(message);
                client.SendMessage(sendMsg, client.ServerConnection, NetDeliveryMethod.ReliableOrdered, 0);
            }


            
        }
        #endregion

        #region Status Changes

        #endregion

    }
}
